import json
import sys
import time

from PySide6.QtWidgets import QWidget, QGridLayout, QLabel, QTextEdit, QComboBox, QPushButton, QApplication, \
    QMessageBox, QLineEdit
from easy_tools.utils.json_to import parse_json_metadata, to_hump, to_char_sep_lower
from easy_tools.ui.utils.code_text_editor_widget import CodeTextEdit


class JsonToCodeWidget(QWidget):

    def __init__(self):
        super().__init__()
        self._init()

    def _init(self):
        self._type_to_java = {
            str: {
                'import': '',
                'class': 'String'
            },
            int: {
                'import': '',
                'class': 'Long',
            },
            float: {
                'import': 'java.math.BigDecimal',
                'class': 'BigDecimal'
            },
            bool: {
                'import': '',
                'class': 'Boolean'
            },
            object: {
                'import': '',
                'class': 'Object'
            },
            list: {
                'import': 'java.util.List',
                'class': 'List',
            },
            tuple: {
                'import': 'java.util.List',
                'class': 'List',
            }
        }
        self._type_to_python = {
            str: {
                'import': '',
                'class': 'str'
            },
            int: {
                'import': '',
                'class': 'int'
            },
            float: {
                'import': '',
                'class': 'float'
            },
            bool: {
                'import': '',
                'class': 'bool'
            },
            object: {
                'import': '',
                'class': 'object'
            },
            list: {
                'import': 'from typing import List',
                'class': 'List',
            },
            tuple: {
                'import': 'from typing import Tuple',
                'class': 'Tuple',
            }
        }
        self._type_to_mysql = {
            str: {
                'type': 'VARCHAR(255)',
            },
            int: {
                'type': 'BIGINT',
            },
            float: {
                'type': 'DECIMAL(14, 6)',
            },
            bool: {
                'type': 'TINYINT(1)',
            },
            object: {
                'type': 'TEXT'
            },
            list: {
                'type': 'TEXT'
            },
            tuple: {
                'type': 'TEXT'
            },
            dict: {
                'type': 'TEXT'
            }
        }
        self.support_language = ['java-lombok', 'mysql', 'python-pydantic']
        self.column_types = ['驼峰', '下划线小写', '中划线小写']
        grid = QGridLayout(self)
        self.setLayout(grid)
        self.input_widget = CodeTextEdit('', self)
        self.json_check_widget = QLabel('不是有效的JSON', self)
        self.json_check_widget.setStyleSheet("color: red")
        self.json_check_widget.hide()
        self.choice = QComboBox(self)
        self.choice.addItems(self.support_language)
        self.column_type_widget = QComboBox(self)
        self.column_type_widget.addItems(self.column_types)
        self.confirm = QPushButton('生成', self)
        self.output_widget = QTextEdit('', self)
        self.extend_widget = QWidget(self)
        self.extend_meta = {}

        x = 0
        grid.addWidget(QLabel('JSON: ', self), x, 0)
        grid.addWidget(self.input_widget, x, 1)
        grid.addWidget(QLabel('输出: ', self), x, 2)
        grid.addWidget(self.output_widget, x, 3)
        x += 1
        grid.addWidget(self.json_check_widget, x, 1)
        x += 1
        grid.addWidget(QLabel('生成语言: ', self), x, 0)
        grid.addWidget(self.choice, x, 1)
        grid.addWidget(QLabel('字段规则: ', self), x, 2)
        grid.addWidget(self.column_type_widget, x, 3)
        x += 1
        # 拓展組建
        self.extend_height = x

        x += 1
        grid.addWidget(self.confirm, x, 0, 1, 4)

        self.choice.currentIndexChanged.connect(self._choice_exchange)
        self.input_widget.textChanged.connect(self._json_check)
        self.confirm.clicked.connect(self._generate)
        self._choice_exchange()

    def _init_java_extend(self):
        self._close_extend()
        self.extend_widget = QWidget(self)
        java_extend_layout = QGridLayout(self.extend_widget)
        self.extend_widget.setLayout(java_extend_layout)
        self.extend_meta = {
            'java_package': QLineEdit('com.xianyu.tools', self.extend_widget),
            'java_author': QLineEdit('dragons', self.extend_widget),
            'java_main_class': QLineEdit('JavaRootBean', self.extend_widget),
        }
        java_extend_layout.addWidget(QLabel('包名: ', self.extend_widget), 0, 0)
        java_extend_layout.addWidget(self.extend_meta['java_package'], 0, 1)
        java_extend_layout.addWidget(QLabel('作者: ', self.extend_widget), 0, 2)
        java_extend_layout.addWidget(self.extend_meta['java_author'], 0, 3)
        java_extend_layout.addWidget(QLabel('主类名: ', self.extend_widget), 1, 0)
        java_extend_layout.addWidget(self.extend_meta['java_main_class'], 1, 1)

        self.layout().addWidget(self.extend_widget, self.extend_height, 0, 1, 4)

    def _init_python_extend(self):
        self._close_extend()
        self.extend_widget = QWidget(self)
        python_extend_layout = QGridLayout(self.extend_widget)
        self.extend_widget.setLayout(python_extend_layout)
        self.extend_meta = {
            'py_base_model': QLineEdit('from pydantic import BaseModel', self.extend_widget),
            'py_author': QLineEdit('dragons', self.extend_widget),
            'py_main_class': QLineEdit('PydanticModel', self.extend_widget)
        }
        python_extend_layout.addWidget(QLabel('BaseModel来源: ', self.extend_widget), 0, 0)
        python_extend_layout.addWidget(self.extend_meta['py_base_model'], 0, 1)
        python_extend_layout.addWidget(QLabel('作者: ', self.extend_widget), 0, 2)
        python_extend_layout.addWidget(self.extend_meta['py_author'], 0, 3)
        python_extend_layout.addWidget(QLabel('主类名: ', self.extend_widget), 1, 0)
        python_extend_layout.addWidget(self.extend_meta['py_main_class'], 1, 1)

        self.layout().addWidget(self.extend_widget, self.extend_height, 0, 1, 4)

    def _init_mysql_extend(self):
        self._close_extend()
        self.extend_widget = QWidget(self)
        mysql_extend_layout = QGridLayout(self.extend_widget)
        self.extend_meta = {
            'mysql_engine': QLineEdit('INNODB', self.extend_widget),
            'mysql_charset': QLineEdit('utf8mb4', self.extend_widget),
            'mysql_main_table': QLineEdit('mysql_root_table', self.extend_widget),
        }

        mysql_extend_layout.addWidget(QLabel('引擎: ', self.extend_widget), 0, 0)
        mysql_extend_layout.addWidget(self.extend_meta['mysql_engine'], 0, 1)
        mysql_extend_layout.addWidget(QLabel('字符编码: ', self.extend_widget), 0, 2)
        mysql_extend_layout.addWidget(self.extend_meta['mysql_charset'], 0, 3)
        mysql_extend_layout.addWidget(QLabel('主表名: ', self.extend_widget), 1, 0)
        mysql_extend_layout.addWidget(self.extend_meta['mysql_main_table'], 1, 1)

        self.layout().addWidget(self.extend_widget, self.extend_height, 0, 1, 4)

    def _choice_exchange(self):
        text = self.choice.currentText()
        if 'java-lombok' == text:
            self._init_java_extend()
        elif 'mysql' == text:
            self._init_mysql_extend()
        elif 'python-pydantic' == text:
            self._init_python_extend()

    def _json_check(self):
        text = self.input_widget.toPlainText()
        try:
            json.loads(text)
            self.json_check_widget.hide()
        except json.decoder.JSONDecodeError:
            self.json_check_widget.show()

    def _generate(self):
        if not self.input_widget.toPlainText():
            QMessageBox.question(self, '错误', "JSON输入不能为空", QMessageBox.StandardButton.Ok)
            return
        if self.choice.currentText() == 'java-lombok':
            infos = self._parse_json_data(self.extend_meta['java_main_class'].text().strip())
            self._generate_java_lombok(infos)
        elif self.choice.currentText() == 'mysql':
            infos = self._parse_json_data(self.extend_meta['mysql_main_table'].text().strip())
            self._generate_mysql_sql(infos)
        elif self.choice.currentText() == 'python-pydantic':
            infos = self._parse_json_data(self.extend_meta['py_main_class'].text().strip())
            self._generate_python_pydantic(infos)
        else:
            QMessageBox.question(self, '错误', "未知选择", QMessageBox.StandardButton.Ok)

    def _parse_json_data(self, name):
        data = json.loads(self.input_widget.toPlainText())
        return parse_json_metadata(name, data)

    def _generate_java_lombok(self, infos):
        package_name = self.extend_meta['java_package'].text().strip()
        author = self.extend_meta['java_author'].text().strip()
        if package_name.endswith(';'):
            package_name = package_name[:-1]
        code = f'''package {package_name};\n\nimport lombok.Data;\n'''
        type_set = set()

        class_code = ''
        for info in infos:
            class_code += f"""\n/**
 * XianYu auto-generated
 *
 * @author {author}
 * @since {time.strftime("%Y/%m/%d %H:%M")}
 */\n"""
            class_code += '@Data\n'
            class_code += 'class ' + to_hump(info['class']) + ' {\n\n'
            for field in info['fields']:
                field_name = self._get_field_name(field["field"])
                class_code += f"""    /**
     * {field_name}
     */\n"""
                if field['type'] in (dict,):
                    class_code += f'    private {to_hump(field["field"])} {field_name};\n\n'
                elif field['type'] in (tuple, list):
                    off = 0
                    class_code += f'    private {self._type_to_java[field["type"]]["class"]}'
                    for generic in field['generics']:
                        off += 1
                        if generic in (dict,):
                            class_code += '<' + to_hump(field['field'])
                        else:
                            class_code += '<' + self._type_to_java[generic]['class']
                    for i in range(off):
                        class_code += '>'
                    class_code += f" {field_name};\n\n"
                else:
                    class_code += f'    private {self._type_to_java[field["type"]]["class"]} {field_name};\n\n'
                type_set.add(field['type'])
                for generic in field['generics']:
                    type_set.add(generic)
            class_code += "}\n"

        if dict in type_set:
            type_set.remove(dict)

        for tp in type_set:
            if self._type_to_java[tp]['import']:
                code += 'import ' + self._type_to_java[tp]['import'] + ';\n'

        code += class_code
        self.output_widget.setText(code)

    def _generate_python_pydantic(self, infos):
        base_model_import = self.extend_meta['py_base_model'].text().strip()
        base_model_name = base_model_import.split('import')[-1].strip()
        author = self.extend_meta['py_author'].text().strip()
        code = f'''{base_model_import}\n'''
        type_set = set()
        class_code = ''
        for info in infos:
            class_code += f'''\n\nclass {to_hump(info['class'])}({base_model_name}):
    """
    XianYu auto-generated
    
    Author: {author}
    Date: {time.strftime("%Y/%m/%d %H:%M")}
    """\n\n'''
            for field in info['fields']:
                field_name = self._get_field_name(field["field"])
                class_code += f'    # \n'
                if field['type'] in (dict,):
                    class_code += f'    {field_name}: {to_hump(field["field"])}\n'
                elif field['type'] in (tuple, list):
                    off = 0
                    class_code += f'    {field_name}: {self._type_to_python[field["type"]]["class"]}'
                    for generic in field['generics']:
                        off += 1
                        if generic in (dict,):
                            class_code += '[' + to_hump(field['field'])
                        else:
                            class_code += '[' + self._type_to_python[generic]['class']
                    for i in range(off):
                        class_code += ']'
                    class_code += '\n'
                else:
                    class_code += f'    {field_name}: {self._type_to_python[field["type"]]["class"]}\n'
                type_set.add(field['type'])
                for generic in field['generics']:
                    type_set.add(generic)

        if dict in type_set:
            type_set.remove(dict)

        for tp in type_set:
            if self._type_to_python[tp]['import']:
                code += f'{self._type_to_python[tp]["import"]}\n'

        code += class_code
        self.output_widget.setText(code)

    def _generate_mysql_sql(self, infos):
        code = ''
        engine = self.extend_meta['mysql_engine'].text().strip()
        charset = self.extend_meta['mysql_charset'].text().strip()
        for info in infos:
            code += f"""\nCREATE TABLE IF NOT EXISTS {to_char_sep_lower(info["class"], "_")}(\n"""
            columns_code = ''
            has_id = False
            for i in range(len(info['fields'])):
                field = info['fields'][i]
                # 忽略特定类型
                if field['type'] in (dict, list, tuple):
                    continue
                field_name = self._get_field_name(field["field"])
                if 'id' == field_name.lower():
                    has_id = True
                if i:
                    columns_code += f",\n"
                columns_code += f"    {field_name} {self._type_to_mysql[field['type']]['type']}"
            # 没有ID则生成ID
            if not has_id:
                columns_code = f'    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,\n' + columns_code
            # 有父节点则加上父节点ID
            if info['parent']:
                columns_code += f',\n    {self._get_field_name(info["parent"] + "Id")} BIGINT UNSIGNED NOT NULL'
            # 自动生成的ID自动添加主键
            if not has_id:
                columns_code += f',\n    PRIMARY KEY (`id`)'
            code += columns_code + f'\n) ENGINE={engine} DEFAULT CHARSET={charset};\n'
        self.output_widget.setText(code)

    def _get_field_name(self, name):
        column_type = self.column_type_widget.currentText()
        if column_type == '驼峰':
            return to_hump(name, False)
        elif column_type == '下划线小写':
            return to_char_sep_lower(name, '_')
        elif column_type == '中划线小写':
            return to_char_sep_lower(name, '-')
        return name

    def _close_extend(self):
        if self.extend_widget:
            self.layout().removeWidget(self.extend_widget)
            self.extend_widget.close()

    def close(self) -> bool:
        if self.extend_widget:
            self.extend_widget.close()
        return super().close()


if __name__ == '__main__':
    app = QApplication()
    win = JsonToCodeWidget()
    win.show()
    sys.exit(app.exec())
